// Copyright Notice:
//				input_select.js v1.0.2
//			Copyright@2008-2009 - OpenSiteMobile
//				All rights reserved
// ==========================================================================
//			http://www.opensitemobile.com
// ==========================================================================
// Contact Information:
//			Author: Dwight Vietzke
//			Email:  dwight_vietzke@yahoo.com

/*
	OpenSiteMobile 'text input' selection/range functions
	and several 'iframe' related selection/range functions
	w/dojo integration
*/

dojo.provide("msos.input_select");
dojo.require("msos.selection");


// --------------------------
// Get Selection start/end points (text input fields)
// --------------------------

msos.input_select.input_get_str_end = function (elm) {
	var cursor_pos = { start : 0, end : 0, pos_debug : "\nstart/end -> na" };
	var start_end_debug = "\nMethod: ";

	if (elm.selectionStart) {	// for Mozilla/Opera/Safari: input text/textarea
		cursor_pos.start = elm.selectionStart;
		cursor_pos.end =   elm.selectionEnd;
		start_end_debug += 'selectionStart-selectionEnd';
	} else if (elm.createTextRange && document.body.createTextRange) {	// for IE: input text/textarea
		var start = 0;
		var end = 0;
		var body_start = 0;
		var body_end = 0;
		var elm_end = 0;
		var inp_rng = null;
		start_end_debug += 'moveStart-moveEnd IE';

		// Test for 'selection' (this thru an error for input field without any text)
                if (document.selection) {
                        inp_rng = document.selection.createRange();
                }
                if (inp_rng) {
                        while (inp_rng.moveStart("character", -1) != 0) start++;
                        while (  inp_rng.moveEnd("character", -1) != 0) end++;
                }
		start_end_debug += "\nDoc range: str - > " + start + ', end -> ' + end;

		var elm_rng = elm.createTextRange();
		while (elm_rng.moveEnd("character", -1)	!= 0) elm_end++;
		start_end_debug += "\nElm range: elm (real) end - > " + elm_end;

		var bod_rng = dojo.body().createTextRange();
		bod_rng.moveToElementText(elm);
   		while (bod_rng.moveStart("character", -1) != 0) body_start++;
   		while (  bod_rng.moveEnd("character", -1) != 0) body_end++;
		start_end_debug += "\nBody range: str - > " + body_start + ', end -> ' + body_end;

		var is_textarea = (elm.nodeName.toLowerCase() != 'input')	? true : false;
		var is_balanced = (body_end - elm_end == body_start)		? true : false;
		if ((start > 0)	&& is_textarea && is_balanced) { start -= body_start; }
		if ((end > 0)	&& is_textarea && is_balanced) { end -=	  body_start; }

		cursor_pos.start = start;
		cursor_pos.end =     end;
	}
	start_end_debug = "\n\nGet str/end"
		+ ': str -> '	+ cursor_pos.start
		+ ', end -> '	+ cursor_pos.end
		+ start_end_debug;

        cursor_pos.pos_debug = start_end_debug;
	cursor_pos.start = cursor_pos.start > 0 ? cursor_pos.start : 0;
	cursor_pos.end =   cursor_pos.end >   0 ? cursor_pos.end   : 0;
	cursor_pos.end =  (cursor_pos.end < cursor_pos.start) ? cursor_pos.start : cursor_pos.end;

	return cursor_pos;
}


// --------------------------
// Insert/Delete Text at Cursor/Selection (text input fields)
// --------------------------

msos.input_select.text_ins_del = function (inp_elm) {
        var self = this;
        this.input = inp_elm;
        this.cursor = {};
        this.debug = '';

        // Sets focus, no selection
        this.input_set_focus = function () {
                self.input.focus();
                self.debug += "\nSet focus"
        }

        // Sets focus and sets cursor obj from current selection
        this.input_init_focus = function () {
                self.input.focus();
                self.cursor = msos.input_select.input_get_str_end(self.input);
                self.debug += self.cursor.pos_debug
                        + "\nInit focus: str -> " + self.cursor.start
                        + ', end -> ' + self.cursor.end;
        }

        this.input_get_selection = function() {
                // IE check, Opera uses '\r\n', but calculates position correctly
                var tmp = document.selection && !window.opera ? self.input.value.replace(/\r/g,"") : self.input.value;
                return tmp.substring(self.cursor.start, self.cursor.end);
        }

        this.input_set_cursor = function(start, end) {
                var inp_debug = "\nSet Method: ";
                if (!start) {	// Set at end of avail text, regardless of 'dir'
                	start =	self.input.value.length;
                	end =	self.input.value.length;
                }
                if (self.input.setSelectionRange) {
                        // scroll by simulating a user keypress (Moz only)
                        if (window.KeyEvent) {
                               self.input.setSelectionRange(start, start+1);
                                var ev = document.createEvent('KeyEvents');
                                var cc = self.input.value.charCodeAt(start);
                                ev.initKeyEvent('keypress', true, true, null, false, false, false, false, 0, cc);
                                self.input.dispatchEvent(ev);
                        }
                        // Now set the 'real' selection range
                        self.input.setSelectionRange(start, end);
                        inp_debug += 'setSelectionRange';
                } else if (self.input.createTextRange) {
                        var inp_rng = self.input.createTextRange();
                        inp_rng.collapse(true);
                        inp_rng.moveStart("character", start);
                        inp_rng.moveEnd("character", end - start);
                        inp_rng.scrollIntoView(true);
                        inp_rng.select();
                        inp_debug += 'move';
                  }
                inp_debug += "\nLast char code: " + self.input.value.charCodeAt(start - 1);
                self.debug += "\nSet cursor: start -> " + start + ', end -> ' + end + inp_debug;
        }

        this.input_del_character = function (is_del) {
                if (!is_del) is_del = false;
                var inp_val = self.input.value;
                var start = self.cursor.start;
                var end =   self.cursor.end;
                var tmp = '';
                if (start == end) {
                        var char_length = 1;
                        var char_adj = 0;
                        if (window.opera) {
                                if (is_del)	{ char_adj += (inp_val.charCodeAt(start + 1) == 10) ? 1 : 0; }
                                else		{ char_adj += (inp_val.charCodeAt(start - 1) == 10) ? 1 : 0; }
                        }
                        // Delete char before (is_del == false), or after cursor (is_del == true)
                        if (is_del)	self.debug += "\nChar code at delete: "	 + inp_val.charCodeAt(start + 1);
                        else		self.debug += "\nChar code at bkspace: " + inp_val.charCodeAt(start - 1);
                        char_length += char_adj;
                        start =	is_del ? start : start - char_length < 0 ? 0 : start - char_length;
                        end =	is_del ? end + char_length : end;
                }
                // IE: need to correct for '\r\n' cursor position counted as just '\n'
                tmp = document.selection && !window.opera ? inp_val.replace(/\r/g,"") : inp_val;

                self.input.value = tmp.substring(0, start) + tmp.substring(end, tmp.length);
                self.input_set_cursor(start, start);
        }

        this.input_ins_character = function (txt) {
                if (!txt) return;
                var inp_val = self.input.value;
                var txt_length = txt.length;
                var txt_adj = 0;
                var set_curs = 0;
                var tmp = '';
                // Opera: need to adjustment for '\n' -> '\r\n' of input text
                if (window.opera) {
                        for(var i=0; i<txt.length; i++) { txt_adj += (txt.charCodeAt(i) == 10) ? 1 : 0; }
                        txt_length += txt_adj;
                }
                // IE: need to correct for '\r\n' cursor position counted as just '\n'
                tmp = document.selection && !window.opera ? inp_val.replace(/\r/g,"") : inp_val;

                self.input.value = tmp.substring(0, self.cursor.start) + txt + tmp.substring(self.cursor.end, tmp.length);
                if (self.cursor.end > self.cursor.start)	{ set_curs = self.cursor.start + txt_length; }
                else						{ set_curs = self.cursor.end +	 txt_length; }
                self.input_set_cursor(set_curs, set_curs);
        }
}


// --------------------------
// Insert HTML (editable elements)
// --------------------------

// Insert html into cursor position (replacing selection if any)
// Note: code use is limited to editable elements
msos.input_select.edit_insert_html = function(win, doc, ifr, html) {
        var debug_txt = "edit_insert_html -> ";
        var sel_obj = new msos.selection.generate_object(win, doc, ifr);
	var win_sel = sel_obj.get_selection();
	var win_range = sel_obj.get_range(win_sel);
        // If no cursor or selection, quit
	if (!win_range) return;
	if (window.getSelection) {
                debug_txt += 'Moz & Opera way';
                doc.execCommand('inserthtml', false, html);
	} else if (document.selection) {
                debug_txt += 'IE way';
		if (win_range.pasteHTML) {
			if (win_sel.type.toLowerCase() != "none") win_sel.clear();
			win_range.pasteHTML(html);
		} else {
			doc.execCommand('inserthtml', false, html);
		  }
	  }
        sel_obj.set_focus();
        console.debug(debug_txt + ", Completed!");
}


// --------------------------
// Javascript backspace/delete function (editable elements)
// --------------------------

msos.input_select.set_bksp_del_select = function(win, doc, ifr, bksp_rng) {
        var debug_txt = "set_bksp_del_select -> ";
        var char_move = 0;
        var flag_select = false;
        var tag = 'body';

        // Get the current selection start/end/target etc cursor info.
        var sel_obj = new msos.selection.generate_object(win, doc, ifr);

        // Set true for extensive debugging (may disrutp cursor reset)
        sel_obj.debug = false;

        // Get a new cursor definition object
        var cursor = sel_obj.selection_str_end();

        // Should we continue
        if (typeof(cursor) != "object") return;
	if (cursor.target && cursor.target.nodeName) tag = cursor.target.nodeName.toLowerCase();
	if(/body|html|head|base/.test(tag)) return;

        // Set default to delete character
	if (typeof(bksp_rng) == "undefined") bksp_rng = false;

        var start = cursor.start + cursor.offset;
        var end =   cursor.end +   cursor.offset;

        // Sort out logic for 'what to do'
        if (start != end) {             // Selected text, so just remove it
                flag_select = true;
        } else if (bksp_rng) {          // Simulate backspace
                if (start > 0) {
                        start--;
                        char_move = -1;
                        flag_select = true;
                }
        } else {                        // Simulate delete
                if (end < cursor.max) {
                        end++;
                        char_move = 1;
                        flag_select = true;
                }
          }

        if (window.getSelection) {
                debug_txt += "window.getSelection: ";

                if (flag_select) {

                        // Create a new range with given start, end...
                        cursor.range = sel_obj.set_start_end(cursor.target, start, end);

                        // Set focus back to selection
                        sel_obj.set_focus();

                        // Add new range to the current selection
                        cursor.select.removeAllRanges();
                        cursor.select.addRange(cursor.range);

                        // Now do the backspace/delete part
                        sel_obj.remove();

                        if (dojo.isOpera) {
                                // For some reason, the 'remove' above leaves the cursor in the
                                // wrong location (+1), so we force it back to the right place
                                cursor.range = sel_obj.set_start_end(cursor.target, start, start);
                                sel_obj.set_focus();
                                cursor.select.removeAllRanges();
                                cursor.select.addRange(cursor.range);
                        }
                }
        } else if (document.selection) {
                debug_txt += "document.selection: ";

                if (flag_select) {

                        // Create a new range with given start, end...
                        cursor.range = sel_obj.set_start_end(cursor.target, start, end);

                        // Set focus back to selection
                        sel_obj.set_focus();

                        // Set range to the current selection
                        cursor.range.select();

                        // Now do the backspace/delete part
                        sel_obj.remove();
                }
          }
        // For advanced debugging -> initial selection vs. final one
        if (sel_obj.debug) sel_obj.selection_str_end();
        console.debug(debug_txt + 'moved ' + char_move);
}

// --------------------------
// Insert html after a target element
// --------------------------

msos.input_select.insert_adj_html = function(target, win, html) {
	if (target.insertAdjacentHTML) {
		target.insertAdjacentHTML('afterend', html);
	} else {
		// Gecko,NS,Mozilla code for insertAdjacentHTML 
		var rng = win.document.createRange();
		var fragment = rng.createContextualFragment(html);
		if (target.nextSibling)	target.parentNode.insertBefore(fragment, target.nextSibling);
		else			target.parentNode.appendChild(fragment);
	  }
}


// --------------------------
// Helper functions
// --------------------------

// Returns the deepest node that contains selection (allow 'body' true|false).
msos.input_select.get_sel_parent_element = function(win, doc, ifr, allow_body) {
        if (!allow_body) allow_body = false;

        var sel_obj = new msos.selection.generate_object(win, doc, ifr);
	var sel_par = sel_obj.getParentElement() || null;

        if (sel_par) {
                if      (!/html|head|base|body/i.test(sel_par.nodeName))                { return sel_par; }
                else if ((!/html|head|base/i.test(sel_par.nodeName) && allow_body))     { return sel_par; }
                else                                                                    { return null;    }
        } else                                                                          { return null;    }
}

msos.input_select.scroll_test = function(if_el, node) {
        var pos = {
                absLeft : 0,
                absTop : 0
        };
        var scroll_top = 1;
        var scroll_lft = 1;
        var h = if_el.clientHeight;
        var w = if_el.clientWidth;

        while (node) {
                pos.absLeft += node.offsetLeft;
                pos.absTop  += node.offsetTop;
                node = node.offsetParent;
        }

	// Only scroll if out of visible area
        var grt_top = (pos.absTop > h);
        var grt_lft = (pos.absLeft > w);

	if (grt_top || grt_lft) { return true;  }
        else                    { return false; }
}